package com.ebrg.secure9.mail.security;


import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;


import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.util.encoders.Hex;

import android.content.Context;
import android.widget.Toast;

import com.ebrg.secure9.R;
import com.ebrg.secure9.activity.setup.EncryptionPin;

public class Hash
{

	private static boolean match;
	private static ArrayList<String> pinList = new ArrayList<String>(2);
	private static String[] pinArray;

	public Hash(){}
	
	public static void refreshPins(Context context)
	{
		pinList.clear();
		//Load files into key array (all files)
		pinArray = context.fileList();
		
		//Search array for .key files, adding them to the keyList.
		for(int i = 0; i < pinArray.length; i++)
			if(pinArray[i].endsWith(context.getString(R.string.legacy_pin_file_type)) || pinArray[i].endsWith(context.getString(R.string.hashed_pin_file_type)))
				pinList.add(pinArray[i]);		
	}
	
	//Find pin for any given remote email account
	public static boolean pinExists(Context context, String localEmail)
	{
		refreshPins(context);		
		if(pinList.contains(localEmail + context.getString(R.string.legacy_pin_file_type)) || 
				pinList.contains(localEmail + context.getString(R.string.hashed_pin_file_type)))
			return true;
		else
			return false;
	}
	
	//Used for generating new hash with only new pin input
	public static String setHash(String localEmail, char[] password, Context context)
	{
		SHA256Digest digest = new SHA256Digest();
		String randSalt = getSalt();
		int length = localEmail.length() + password.length + randSalt.length();
		char[] combined = new char[length];
		
		//Concat everything into one char array (password handling at this point,
		//so we avoid the use of Strings.
		System.arraycopy(localEmail.toCharArray(), 0, combined, 0, localEmail.length());
		System.arraycopy(password, 0, combined, localEmail.length(), password.length);
		System.arraycopy(randSalt.toCharArray(), 0, combined, localEmail.length() + password.length, randSalt.length());
	    
		digest.update(charsToBytes(combined), 0, charsToBytes(combined).length);			
		byte[] hash = new byte[digest.getDigestSize()];
		digest.doFinal(hash, 0);

		String outHash = new String(Hex.encode(hash));

		//Place random salt in middle of combined.  (password.length characters in)
		if(password.length < outHash.length() - 32)
		{
			String first = outHash.substring(0, password.length);
			String last = outHash.substring(password.length, outHash.length());		
			outHash = first + randSalt + last;
		}
		//Only if user has pin 30+ characters long... so it doesn't overwrite static.
		else
		{
			String first = outHash.substring(0, 4);
			String last = outHash.substring(4, outHash.length());		
			outHash = first + randSalt + last;
		}
		return outHash;
	}

	//When checking input pin against stored hash, we call getStatic and getRandom,
	//then verify passing all values.  This will recompute the hash and return a new
	//hash from the input.  This will be compared against the stored string.
	public static String verify(String localEmail, char[] password, String randSalt, String staticSalt)
	{
		if(password.length < 4)
			return "";
		else
		{
			SHA256Digest digest = new SHA256Digest();
			int length = localEmail.length() + password.length + randSalt.length();
			char[] combined = new char[length];
			
			//Concat everything into one char array (password handling at this point,
			//so we avoid the use of Strings.
			System.arraycopy(localEmail.toCharArray(), 0, combined, 0, localEmail.length());
			System.arraycopy(password, 0, combined, localEmail.length(), password.length);
			System.arraycopy(randSalt.toCharArray(), 0, combined, localEmail.length() + password.length, randSalt.length());
		    
			digest.update(charsToBytes(combined), 0, charsToBytes(combined).length);			
			byte[] hash = new byte[digest.getDigestSize()];
			digest.doFinal(hash, 0);		
			String outHash = new String(Hex.encode(hash));

			String first = outHash.substring(0, password.length);
			String last = outHash.substring(password.length, outHash.length());		
			outHash = first + randSalt + last;
			Arrays.fill(combined, '0');
			return outHash;
		}

	}
	
	 public static byte[] charsToBytes(char[] combined) 
	 {
	        byte[] bytes = new byte[combined.length];		        
	        for (int i = 0; i < combined.length; i++) 
	            bytes[i] = (byte)(combined[i] & 0xFF);		        
	        return bytes;
	  }

	//Returns last 32 chars of STORED pin in hex (random salt)
	public static String getRandom(String fullHashWithSalts, int pinLength, Context context)
	{	
		if(pinLength >= 4 && pinLength + 32 < fullHashWithSalts.length())
		{
			//Retrieve salt from stored hash, starting from
			//pinLength characters in, to pinlength + 32. 
			String randomSalt = fullHashWithSalts.substring(pinLength, pinLength + 32);		
			return randomSalt;
		}
		//Only if user has pin 30+ characters long... so it doesn't overwrite static.
		else if(pinLength >= 4 && pinLength >= fullHashWithSalts.length() - 32 ){
			//Retrieve salt from stored hash, starting from
			//pinlength characters in, to pinlength + 32. 
			String randomSalt = fullHashWithSalts.substring(4, 4 + 32);		
			return randomSalt;
		}
		else
			return "";

	}

	//Returns last 32 characters in STORED pin (static salt)
	public static String getStatic(String fullHashWithSalts, int pinLength, Context context)
	{
		if(fullHashWithSalts != null && fullHashWithSalts.length() > 32 && pinLength >= 4)
		{
			String staticSalt = fullHashWithSalts.substring(fullHashWithSalts.length()-32,
					fullHashWithSalts.length());
			return staticSalt ;
		}
		else 
			return "";
	}

	public static String getSalt()
	{
		SecureRandom r = new SecureRandom();
		byte[] salt = new byte[16];
		r.nextBytes(salt);
		String strSalt = new String(Hex.encode(salt));
		EncryptionPin.randomSaltLength = strSalt.length();
		return strSalt;		
	}

	public static boolean compareHashes(String inputHash, String storedHash)
	{
		if(storedHash.equals(inputHash))
			return true;
		else
			return false;
	}

	public static boolean convertPin(String localEmail, Context context)
	{
		Toast.makeText(context, "Unhashed PIN found.", Toast.LENGTH_LONG).show();

		String newHash = "";

		//convert old unhashed .pin into hashed .pin2
		//Any time pin verification is done, we'll need to
		//check for .pin2.  If not .pin2, but .pin, call this 
		//method.  Convert old pin and save as .pin2, delete .pin.

		//Read in old pin
		try 
		{
			FileInputStream fis = context.openFileInput(localEmail + context.getString(R.string.legacy_pin_file_type));
			BufferedReader in = new BufferedReader(new InputStreamReader(fis));	
			int r;
			char[] oldPin = new char[150];
			int index = 0;
			while ((r = in.read()) != -1) 
			{
				oldPin[index] = (char) r;
				index++;
			}
			
			//Add pin to "fitted" array.
			char[] pin = new char[index];
			for(int i = 0; i < pin.length; i++)
				pin[i] = oldPin[i];


			//Hash old pin
			newHash = Hash.setHash(localEmail.toLowerCase(), pin, context);

			//Save hash as new .pin2
			String FILENAME = (localEmail.toLowerCase()  + context.getString(R.string.hashed_pin_file_type));
			FileOutputStream fos1 = context.openFileOutput(FILENAME, Context.MODE_PRIVATE);
			fos1.write(newHash.getBytes());
			fos1.close();

			//Delete old pin file
			context.deleteFile(localEmail + context.getString(R.string.legacy_pin_file_type));
			Toast.makeText(context, "PIN converted to SHA256 hash", Toast.LENGTH_LONG).show();


		} catch (FileNotFoundException e) {
			return false;
		} catch (IOException e) {
			return false;
		}

		return true;	
	}

}